package core.pathfinder;

import game.scenario.Map;

import java.util.Iterator;
import java.util.Vector;

import utils.DBG;
import utils.Properties;

public class Pathfinder {

	private Map map;
	private boolean navigation[][];
	private RoutePoint pathnodes[][];
	private Vector<RoutePoint> open;
	private Vector<RoutePoint> closed;
	private static Pathfinder instance;

	public static Pathfinder getInstance() {
		if (instance == null) {
			instance = new Pathfinder();
		}
		return instance;
	}

	private Pathfinder() {
		open = new Vector<RoutePoint>();
		closed = new Vector<RoutePoint>();
	}

	public void setMap(Map map_) {
		map = map_;
	}

	public Route getRoute(int beginx, int beginy, int endx, int endy) {
		if (map != null) {

			open = new Vector<RoutePoint>();
			closed = new Vector<RoutePoint>();
			navigation = map.getNavigationMatrix();
			pathnodes = new RoutePoint[navigation.length][navigation[0].length];
			
			Route route = new Route();
			//this.initPathnodes(beginx, beginy); // iniciamos todos los pathnodes
			// salvo en
			// los que no hay navegacion.
			
			//begin pathnode
			pathnodes[beginx][beginy] = new RoutePoint(beginx * Properties.TILE_WIDTH , beginy * Properties.TILE_HEIGHT);
			//end pathnode
			pathnodes[endx][endy] = new RoutePoint(endx * Properties.TILE_WIDTH , endy * Properties.TILE_HEIGHT);
				
			RoutePoint actual = pathnodes[beginx][beginy];
			RoutePoint objetivo = pathnodes[endx][endy];

			int tx = endx;
			int ty = endy;

			while (objetivo != null && actual != null
					&& actual.compareTo(objetivo) != 1) { // condicion
				open.remove(actual);
				closed.add(actual);
				Iterator<RoutePoint> it = doAdyacent(actual.mapx, actual.mapy);
				while (it.hasNext()) {
					RoutePoint rp = it.next();
					if (!open.contains(rp) && !closed.contains(rp)) {
						rp.father = actual;
						rp.calculateG();
						rp.calculateH(tx, ty);
						open.add(rp);
					}
				}
				actual = this.getNextOpen();
				

			}

			route.setBegin(pathnodes[beginx][beginy]);
			route.setEnd(pathnodes[endx][endy]);
			actual = pathnodes[endx][endy].father;
			while (actual != null && actual.father != null) {
				route.addInverse(actual);
				actual = actual.father;
			}
			return route;
		} else {
			return null;
		}
	}

	private void initPathnodes(int beginx, int beginy) {

//		navigation = map.getNavigationMatrix();
//		pathnodes = new RoutePoint[navigation.length][navigation[0].length];
//		for (int cont = 0; cont < navigation.length; cont++) {
//			for (int cont2 = 0; cont2 < navigation[0].length; cont2++) {
//				if (navigation[cont][cont2]) {
//					RoutePoint rp = new RoutePoint(
//							cont * Properties.TILE_WIDTH, cont2
//									* Properties.TILE_HEIGHT);
//
//					pathnodes[cont][cont2] = rp;
//				}
//			}
//		}

		
		for (int cont = beginx - 2; cont < beginx + 2; cont++) {
			for (int cont2 = beginy - 2; cont2 < beginy + 2; cont2++) {
				if ((cont >= 0 && cont < navigation.length)
						&& (cont2 >= 0 && cont2 < navigation[0].length)) {
					if (navigation[cont][cont2]
							&& pathnodes[cont][cont2] == null) {
						RoutePoint rp = new RoutePoint(cont
								* Properties.TILE_WIDTH, cont2
								* Properties.TILE_HEIGHT);

						pathnodes[cont][cont2] = rp;
						DBG.showMsg(".", this);
					}
				}
			}
		}

	}

	private RoutePoint getNextOpen() {
		RoutePoint ret = null;
		Iterator<RoutePoint> it = open.iterator();

		while (it.hasNext()) {
			if (ret == null) {
				ret = it.next();
			} else {
				RoutePoint other = it.next();
				if (other != null) {
					if (ret.getF() >= other.getF()) {
						ret = other;
					}
				}
			}
		}
		return ret;
	}

	private Iterator<RoutePoint> doAdyacent(int xpos, int ypos) {
		Vector<RoutePoint> adyacentes = new Vector<RoutePoint>();
		int trans[][] = { { 1, 0 }, { 1, -1 }, { 0, -1 }, { -1, -1 },
				{ -1, 0 }, { -1, 1 }, { 0, 1 }, { 1, 1 } };
		// int trans[][] = { { 1, 0 }, { 0, -1 }, { -1, 0 },{ 0, 1 } };
		for (int cont = 0; cont < trans.length; cont++) {
			try {
				if (pathnodes[xpos + trans[cont][0]][ypos+ trans[cont][1]] == null){
					pathnodes[xpos + trans[cont][0]][ypos+ trans[cont][1]] = new RoutePoint((xpos + trans[cont][0])*Properties.TILE_WIDTH , (ypos+ trans[cont][1]) *  Properties.TILE_HEIGHT);
				}
				RoutePoint rp = pathnodes[xpos + trans[cont][0]][ypos+ trans[cont][1]];
				if (cont % 2 == 0) {
					rp.C = 10;
				} else {
					rp.C = 15;
				}
				if (rp != null)
					if (navigation[xpos + trans[cont][0]][ypos + trans[cont][1]]){
						adyacentes.add(rp);
					}
			} catch (Exception e) {

			}
		}

		return adyacentes.iterator();
	}

}
